package org.andao.commons.service.impl;

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.andao.commons.cache.CacheAppManager;
import org.andao.commons.model.AccessLogs;
import org.andao.commons.model.Dictionary;
import org.andao.commons.service.AccessLogsService;
import org.andao.core.base.service.BaseServiceImpl;
import org.andao.core.dao.helper.Page;
import org.andao.core.dao.jdbc.JdbcSupportDao;
import org.andao.core.exception.ServiceException;
import org.andao.core.utils.ExDateUtils;
import org.andao.core.utils.ExStringUtils;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


/**
 * 系统访问日志管理服务接口实现. 
 * 
 * @author Marco.hu(hzg139@163.com)
 */
@Transactional
@Service("accessLogsService")
public class AccessLogsServiceImpl extends BaseServiceImpl<AccessLogs> implements AccessLogsService {
	
	@Autowired
	@Qualifier("defaultJdbcSupportDao")
	private JdbcSupportDao jdbcSupportDao;
	
	@Override
	public void parseAndSaveAccessLogs(String fileName) throws Exception {
		List<AccessLogs> logs = new ArrayList<AccessLogs>();
		
		long length = new File(fileName).length();
		MappedByteBuffer in = new RandomAccessFile(fileName,"r").getChannel().map(FileChannel.MapMode.READ_ONLY,0,length);
		CharBuffer bf = CharBuffer.allocate(4048);	
		
		int index = 0;
		while(index < length){
			char c = (char)in.get(index++);				
			if(c == '\r' || c == '\n'){	
				AccessLogs log = parseAccessLogs(new String(bf.array(),0,bf.position()));
				if(log != null){
					logs.add(log);
				}
				if(logs.size() > 10000){
					batchSaveOrUpdate(logs);
					flush();
					logs.clear();
					logs = new ArrayList<AccessLogs>();
				}
				bf.clear();
			}else{
				bf.put(c);
			}
		}
		if(logs.size()>0){
			batchSaveOrUpdate(logs);
		}
	}
	
	
	//解析字串为日志对象
	private AccessLogs parseAccessLogs(String str) throws Exception {
		AccessLogs log = null;
		if(testValidLog(str)){
			StringTokenizer stringTokenizer = new StringTokenizer(str, "$");
			Map<Integer, String> map = new HashMap<Integer, String>();
		
			for (int i = 1; stringTokenizer.hasMoreTokens(); i++) {
				String temp = ExStringUtils.trimToEmpty(stringTokenizer.nextToken());	
				map.put(i, temp);	
			}	
			
			//127.0.0.1$ 
			//admin$
			//[16/Jun/2011:17:16:21 +0800]$ 
			//POST /hnjk/edu3/portal/message/list.html HTTP/1.1$ 
			//200$ 
			//20172$ 
			//1.234$ 
			//Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
			if(null != map && map.size()==8){
				log = new AccessLogs();
				//ip
				log.setIpaddress(map.get(1));
				//用户
				String username = ExStringUtils.trimToEmpty(map.get(2));
				if(!"-".equals(username)){
					log.setUsername(username);
				}
				//日期
				log.setAccessTime(ExDateUtils.parseUSDate(map.get(3),"'['dd/MMM/yyyy:HH:mm:ss Z']'"));
				//资源
				String[] res = map.get(4).split("\\ ");
				if(res != null && res.length==3){									
					log.setUrl(ExStringUtils.substringBefore(ExStringUtils.substringBefore(res[1], "?"), "#"));
					log.setProtocol(res[2]);
				}
				//访问状态
				log.setServerStatus(Integer.parseInt(map.get(5)));
				//流量
				log.setNetFlow(Long.parseLong(map.get(6).replace("-", "0")));
				//处理时间
				log.setRunningTime(Double.parseDouble(map.get(7)));
				//客户端信息
				String bos = map.get(8);
				log.setClientBrowser(getClientBrowserOrOs(bos,"CodeClientBrowser"));
				log.setClientOs(getClientBrowserOrOs(bos,"CodeClientOs"));
			}
		}
		return log;
	}
	
	private boolean testValidLog(String str){
		if(str.indexOf("Googlebot")>-1 || str.indexOf("Yahoo! Slurp China")>-1 || str.indexOf(".html")==-1){
			return false;
		}
		return true;
	}
	//获取客户端浏览器或系统类型
	private String getClientBrowserOrOs(String bos, String dictCode) {
		if(ExStringUtils.isNotEmpty(bos)){
			List<Dictionary> list = CacheAppManager.getChildren(dictCode);
			for (Dictionary dict : list) {
				if(bos.contains(dict.getDictValue())){
					return dict.getDictValue();
				}
			}
		}
		return "OTHER";
	}
	
	@Override
	@Transactional(readOnly=true)
	public Page findAccessLogsByCondition(Map<String, Object> condition, Page objPage) throws ServiceException {
		Map<String,Object> values =  new HashMap<String, Object>();
		String hql = " from "+AccessLogs.class.getSimpleName()+" where 1=1 ";
		hql += " and isDeleted = :isDeleted ";
		values.put("isDeleted", 0);
		
		if(condition.containsKey("serverStatus")){
			hql += " and serverStatus =:serverStatus ";
			values.put("serverStatus", condition.get("serverStatus"));
		}
		try {
			if(condition.containsKey("accessStartTime")){//访问开始时间
				hql += " and accessTime >=:accessStartTime ";
				values.put("accessStartTime", ExDateUtils.parseDate(condition.get("accessStartTime").toString(), ExDateUtils.PATTREN_DATE_TIME));
			}
			if(condition.containsKey("accessEndTime")){//访问结束时间
				hql += " and accessTime <=:accessEndTime ";
				values.put("accessEndTime", ExDateUtils.parseDate(condition.get("accessEndTime").toString(), ExDateUtils.PATTREN_DATE_TIME));
			}
		} catch (ParseException e) {
			throw new ServiceException(e);
		}		
		if(ExStringUtils.isNotEmpty(objPage.getOrderBy())){			
			hql += " order by "+objPage.getOrderBy() +" "+ objPage.getOrder();
		}		
		return exGeneralHibernateDao.findByHql(objPage, hql, values);
	}
	
	@Override
	public List<String> accessLogsDate() throws ServiceException {
		Session session  = exGeneralHibernateDao.getSessionFactory().getCurrentSession();
		String sql = " select distinct  to_char(t.accesstime,'yyyy-MM-dd') accessdate from hnjk_sys_accesslogs t where t.isdeleted=0 ";
		SQLQuery query = session.createSQLQuery(sql);
		return query.list();
	}
	
	@Override
	public List<Map<String, Object>> statAccessLogsStatus(Map<String, Object> condition) throws ServiceException {
		String type = condition.get("type").toString();		
		String accessDate = condition.get("accessDate").toString();
		StringBuffer sb = new StringBuffer();
		
		sb.append(" select sec.sectime, count(distinct s.ipaddress) ipcount, count(s.url) urlcount, sum(nvl(s.netflow,0)) netflowcount from ");
		if("1".equals(type)){	//按日				
			sb.append(" (select to_char(to_date('"+accessDate+"','yyyy-MM-dd')+(level-1)/24,'yyyy-MM-dd HH24') sectime from dual connect by level<=25) sec ");
			sb.append(" left join ");
			sb.append(" (select t.*,to_char(t.accesstime,'yyyy-MM-dd HH24') secdate from andao_sys_accesslogs t where to_char(t.accesstime,'yyyy-MM-dd') ='"+accessDate+"'  ) s"); 
		} else if("2".equals(type)) {//按月			
			sb.append(" (select to_char(to_date('"+accessDate+"','yyyy-MM')+(level-1),'yyyy-MM-dd') sectime from dual connect by level<=to_number(to_char(last_day(to_date('"+accessDate+"','yyyy-MM')),'dd')) ) sec ");
			sb.append(" left join ");
			sb.append(" (select t.*,to_char(t.accesstime,'yyyy-MM-dd') secdate from andao_sys_accesslogs t where to_char(t.accesstime,'yyyy-MM') ='"+accessDate+"'  ) s"); 
		} else {//近三月,近六个月
			sb.append(" (select to_char(to_date('"+accessDate+"','yyyy-MM')+(level-1),'yyyy-MM-dd') sectime from dual connect by level<="+condition.get("days")+" ) sec ");
			sb.append(" left join ");
			sb.append(" (select t.*,to_char(t.accesstime,'yyyy-MM-dd') secdate from andao_sys_accesslogs t where to_char(t.accesstime,'yyyy-MM') >='"+accessDate+"'  ) s"); 
		}
		sb.append(" on sec.sectime = s.secdate ");
		sb.append(" group by sec.sectime ");
		sb.append(" order by sec.sectime ");		
		
		try {
			List<Map<String, Object>> list = jdbcSupportDao.getBaseJdbcTemplate().findForListMap(sb.toString(), null);
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}				
		return null;
	}
	
	@Override
	public List<Map<String, Object>> statAccessLogsResults(String type) throws ServiceException {
		StringBuffer sb = new StringBuffer();
		
		sb.append(" select t."+type+" target,count(*) resultcount from andao_sys_accesslogs t ");
		sb.append(" group by t."+type);
		sb.append(" order by resultcount desc ");
		try {
			List<Map<String, Object>> list = jdbcSupportDao.getBaseJdbcTemplate().findForListMap(sb.toString(), null);
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}				
		return null;
	}
	
	@Override
	public Double statAccessLogsResultsTotal() throws ServiceException {
		String sql = " select count(*) totalresult from andao_sys_accesslogs t ";
		
		try {
			Long total = jdbcSupportDao.getBaseJdbcTemplate().findForLong(sql, null);
			return total.doubleValue();
		} catch (Exception e) {
			e.printStackTrace();
		}				
		return 0.0;
	}
	
	@Override
	public List<Map<String, Object>> statAccessLogsTop(int type) throws ServiceException {
		StringBuffer sb = new StringBuffer();		
		switch (type) {
			case 1://并发访问用户数
				sb.append(" select t.accesstime,count(distinct t.ipaddress) ipcount from andao_sys_accesslogs t ");
				sb.append(" group by t.accesstime ");
				sb.append(" order by ipcount desc ");
				break;
			case 2://访问次数最多的用户
				sb.append(" select t.ipaddress,count(*) ipcount from andao_sys_accesslogs t ");
				sb.append(" group by t.ipaddress ");
				sb.append(" order by ipcount desc ");
				break;
			case 3://访问最多的资源
				sb.append(" select t.url,count(t.url) morecount from andao_sys_accesslogs t ");
				sb.append(" group by t.url ");
				sb.append(" order by morecount desc ");
				break;
			case 4:// 处理最慢的资源
				sb.append(" select t.url,t.runningtime,t.accesstime from andao_sys_accesslogs t ");
				sb.append(" order by t.runningtime desc ");
				break;
			case 5: //错误做多的资源
				sb.append(" select t.url,count(*) count500 from andao_sys_accesslogs t  ");
				sb.append(" where t.serverstatus=500 ");
				sb.append(" group by t.url ");
				sb.append(" order by count500 desc ");
				break;
		}
		try {
			String sql = "select * from ("+sb.toString()+" ) where rownum<11";
			List<Map<String, Object>> list = jdbcSupportDao.getBaseJdbcTemplate().findForListMap(sql, null);
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}				
		return null;
	}
}
